[ORG 0x7c00]
[SECTION .data]
BOOT_MAIN_ADDR equ 0x500

[SECTION .text]
[BITS 16]
global boot_start
boot_start:
    ; 清除屏幕，10号BIOS中断，ax为3
    mov ax, 3
    int 0x10

    ; edi是目标地址
    mov edi, BOOT_MAIN_ADDR
    ; ecx是起始扇区
    mov ecx, 1
    ; 读取扇区数
    mov bl, 2
    ; 调用读取扇区函数
    call read_hd

    mov si, jmp_to_setup
    call print

    jmp BOOT_MAIN_ADDR

read_hd:
    ; 0x1f2 指定读取或者写入的扇区数，即bl
    mov dx, 0x1f2
    mov al, bl
    out dx, al

    ; 0x1f3 iba的0到7位，即ecx的最低8位
    mov dx, 0x1f3
    mov al, cl
    out dx, al

    ; 0x1f4 iba的8到15位，即ecx中的ch
    mov dx, 0x1f4
    mov al, ch
    out dx, al

    ; 0x1f5 iba的16到23位，将ecx右移16位，取cl
    mov dx, 0x1f5
    shr ecx, 16
    mov al, cl
    out dx, al

    ; 0x1f6 iba的24到27位，ecx右移8位，取cl和0F的与运算结果
    mov dx, 0x1f6
    shr ecx, 8
    and cl, 0b1111
    ; 4到7位分别指
    ; 4 0主盘 1从盘，这里取主盘
    ; 5、7位固定为1
    ; 6位 0表示chs模式，1表示LAB模式，这里是LAB模式
    mov al, 0b1110_0000
    or al, cl
    out dx, al
    ; CHS是“柱面-磁头-扇区"(Cylinder-Head-Sector)模式的缩写，它是一种早期的硬盘寻址方式，即通过指定磁盘的柱面、磁头和扇区来完成磁盘的数据访问。
    ; 由于这种寻址方式的物理限制，使得CHS模式在现在的大容量硬盘中已经很少使用了。

    ; 0x1f7命令或者状态端口
    mov dx, 0x1f7
    mov al, 0x20
    out dx, al

    ; 1. 0x00 - 空操作
    ; 2. 0x10 - 校正
    ; 3. 0x20 - 读扇区
    ; 4. 0x30 - 写扇区
    ; 5. 0x40 - 读验证
    ; 6. 0x50 - 格式化轨道
    ; 7. 0x70 - 诊断
    ; 8. 0x90 - 初始化驱动器参数
    ; 9. 0xA0 - 带重试的读扇区
    ; 10. 0xB0 - 带重试的写扇区
    ; 11. 0xC0 - 读长
    ; 12. 0xD0 - 写长

    ; 这里其实比较危险，需要先置零
    xor cx, cx ; 老师的实例代码没有这一行
    mov cl, bl
.start_read:
    ; 因为调用函数可能会破坏cx，所以将其保存到栈中，用完后恢复。
    push cx

    ; 等待硬盘可读
    call wait_hd_prepare
    ; 读取数据
    call read_hd_data

    pop cx
    loop .start_read

    ret

wait_hd_prepare:
    ; 读取0x1f7端口一个字节，然后检查其位数
    ; 即第7位为0，第3位为1，第0位为0即为准备好

    ; gpt的回复
    ; 0x1f7端口的8位状态可以如下解读：

    ; 0. 错误（ERR）：这位会在发生错误时设置为1
    ; 1. 索引（IDX）：这位在控制器的每个数据周期开始时设置为1
    ; 2. 纠正（CORR）：这位在数据被硬件纠正后设置为1
    ; 3. 替换（REP）：这位在硬盘执行重定位操作时设置为1
    ; 4. 推动（PUSH）：这位在硬盘准备好数据后设置为1
    ; 5. 请求服务（DRQ）：这位在硬盘准备好数据或命令后设置为1
    ; 6. 磁盘就绪（DRDY）：这位在硬盘准备好操作后设置为1
    ; 7. 总线忙（BUSY）：这位在硬盘正在处理命令或数据时设置为1
    ; 所以第一位为0表示无错误，第3位为1表示已准备好，第7位为0表示不忙，其他位可忽略
    mov dx, 0x1f7

.check:
    in al, dx
    and al, 0b1000_1000
    cmp al, 0b0000_1000
    jnz .check
    ret

read_hd_data:
    ; 读取0x1f0端口的数据，一次读2个字节
    mov dx, 0x1f0
    mov cx, 256

.read_word:
    in ax, dx
    mov [edi], ax
    add edi, 2
    loop .read_word

    ret

; 如何调用
; mov     si, msg   ; 1 传入字符串
; call    print     ; 2 调用
print:
    ; 功能号是0e，BH是页号，BL是颜色
    ; 详细参数解释可以参考BIOS中断参数.txt
    mov ah, 0x0e
    mov bh, 0
    mov bl, 0x01
.loop:
    mov al, [si]
    cmp al, 0
    jz .done
    int 0x10

    inc si
    jmp .loop

.done:
    ret

jmp_to_setup:
    db "jmp to setup...", 10, 13, 0

times 510 - ($ - $$) db 0
dw 0xaa55